﻿using CommandLine;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Drive.v2;
using Google.Apis.Drive.v2.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WotModSync
{
    class Program
    {
        static int Main(string[] args)
        {

            AppDomain.CurrentDomain.AssemblyResolve += (sender, arg) =>
            {

                String resourceName = "WotModSync." + new AssemblyName(arg.Name).Name + ".dll";
                Console.WriteLine("AssemblyResolve");
                using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
                {
                    Byte[] assemblyData = new Byte[stream.Length];

                    stream.Read(assemblyData, 0, assemblyData.Length);

                    return Assembly.Load(assemblyData);
                }

            };


            Console.CursorVisible = true;

            return Main2(args);
        }

        static int Main2(string[] args)
        {
            return Parser.Default.ParseArguments<CreateOptions, InstallOptions, UpdateOptions, ListOptions, ShowOptions, DeleteOptions, RenameOptions/*, ShareOptions, RemoveShareOptions*/>(args).Return(
                                 (CreateOptions opts) => CreateModPack(opts),
                                 (InstallOptions opts) => InstallModPack(opts),
                                 (UpdateOptions opts) => UpdateModPack(opts),
                                 (ListOptions opts) => ListModPackFiles(opts.ModPackName),
                                 (ShowOptions opts) => ShowModPacks(),
                                 (DeleteOptions opts) => DeleteModPack(opts),
                                 (RenameOptions opts) => RenameModPack(opts),
                                 //(ShareOptions opts) => ShareModPack(opts),
                                 //(RemoveShareOptions opts) => RemoveShareModPack(opts),
                                 errs => 1);
        }



        private static void drawTextProgressBar(int progress, int total)
        {
            //draw empty progress bar
            Console.CursorLeft = 0;
            Console.Write("["); //start
            Console.CursorLeft = 32;
            Console.Write("]"); //end
            Console.CursorLeft = 1;
            float onechunk = 30.0f / total;

            //draw filled part
            int position = 1;
            for (int i = 0; i < onechunk * progress; i++)
            {
                Console.BackgroundColor = ConsoleColor.Green;
                Console.CursorLeft = position++;
                Console.Write(" ");
            }

            //draw unfilled part
            for (int i = position; i <= 31; i++)
            {
                Console.BackgroundColor = ConsoleColor.Black;
                Console.CursorLeft = position++;
                Console.Write(" ");
            }

            //draw totals
            Console.CursorLeft = 35;
            Console.BackgroundColor = ConsoleColor.Black;
            Console.Write(progress.ToString() + " of " + total.ToString() + "    "); //blanks at the end remove any excess
        }

        private static void coloredConsoleWrite(ConsoleColor color, string text)
        {
            ConsoleColor originalColor = Console.ForegroundColor;
            Console.ForegroundColor = color;
            Console.Write(text);
            Console.ForegroundColor = originalColor;
        }

        private static void coloredConsoleWriteLine(ConsoleColor color, string text)
        {
            ConsoleColor originalColor = Console.ForegroundColor;
            Console.ForegroundColor = color;
            Console.WriteLine(text);
            Console.ForegroundColor = originalColor;
        }

        private static void ExecuteLongTask(string description, Action action)
        {
            Console.Write(description);
            ConsoleSpiner spiner = new ConsoleSpiner();
            var task = Task.Factory.StartNew(action);
            while (!task.IsCompleted)
            {
                spiner.Turn();
            }
            Console.SetCursorPosition(Console.CursorLeft, Console.CursorTop);
            Console.WriteLine("done");
        }

        private static TResult ExecuteLongTask<TResult>(string description, Func<TResult> func)
        {
            Console.Write(description);
            ConsoleSpiner spiner = new ConsoleSpiner();
            var task = Task.Factory.StartNew(func);
            while (!task.IsCompleted)
            {
                spiner.Turn();
            }
            Console.SetCursorPosition(Console.CursorLeft, Console.CursorTop);
            Console.WriteLine("done");

            return task.Result;

        }

        private static void ExecuteLongTaskProgressbar<T>(IEnumerable<T> list, Action<T> action)
        {
            int i = 0;
            foreach (var item in list)
            {
                drawTextProgressBar(i + 1, list.Count());
                i++;

                action(item);
            }
            Console.WriteLine("");
        }

        private static string CheckWOTDirectory(string wotDirectory)
        {
            if (string.IsNullOrEmpty(wotDirectory))
            {
                wotDirectory = Properties.Settings.Default.WotGameDirectory;
            }

            if (!Directory.Exists(wotDirectory))
            {
                coloredConsoleWriteLine(ConsoleColor.Red, "ERROR --> WOT Game Directory desn't exits. Directory: " + wotDirectory);
                return null;
            }
            else
            {
                coloredConsoleWriteLine(ConsoleColor.Magenta, "WOT Game Directory : '" + wotDirectory + "'");
                return wotDirectory;
            }
        }

        #region Verbs
        private static int DeleteModPack(DeleteOptions opts)
        {
            string modPackName = opts.ModPackName;

            coloredConsoleWriteLine(ConsoleColor.Yellow, "Deleting ModPack '" + modPackName + "'");
            var googleDrive = new GoogleDrive(modPackName);

            ExecuteLongTask("Deleting existing Files...", () => googleDrive.DeleteAllModPackFiles());

            return 0;
        }

        public static int ShowModPacks()
        {
            var googleDrive = new GoogleDrive("");

            var list = ExecuteLongTask("Getting Modpacks...", () => { return googleDrive.GetModPacks(); });
            Console.WriteLine("Available ModPacks :");
            foreach (string s in list)
            {
                Console.WriteLine("\t" + s);
            }
            Console.WriteLine("");
            return 0;
        }

        public static int CreateModPack(CreateOptions opts)
        {

            string modPackName = opts.ModPackName;
            string wotDirectory = CheckWOTDirectory(opts.Output);

            if (string.IsNullOrEmpty(wotDirectory))
            {
                return 1;
            }

            coloredConsoleWriteLine(ConsoleColor.Yellow, "Creating ModPack '" + modPackName + "'");
            var googleDrive = new GoogleDrive(modPackName);

            ExecuteLongTask("Deleting existing Files...", () => googleDrive.DeleteAllModPackFiles());

            ExecuteLongTask("Creating ModPack Directory...", () => googleDrive.GetOrCreateModPackDirectory());

            if (opts.Empty) return 1;

            var files = ExecuteLongTask("Getting local Files...", () => { return WotFiles.GetWotFiles(wotDirectory); });
            Console.WriteLine("Start Uploading...Total {0}", files.Count);

            ExecuteLongTaskProgressbar(files, (file) =>
            {
                Trace.WriteLine("Upload File:" + file.FullName);
                googleDrive.UploadFile(file, wotDirectory);
            });

            Console.WriteLine("Finished. ModPack '" + modPackName + "' ready!");

            return 0;
        }

        public static int ListModPackFiles(string modPackName)
        {
            var googleDrive = new GoogleDrive(modPackName);

            var list = ExecuteLongTask("Getting Modpacks...", () => { return googleDrive.GetModPackFileNames(); });

            Console.WriteLine("Listing ModPack '" + modPackName + "' Files:");
            foreach (string s in list)
            {
                Console.WriteLine(s);
            }
            Console.WriteLine("");
            return 0;
        }

        public static int InstallModPack(InstallOptions opts)
        {
            string modPackName = opts.ModPackName;
            string wotDirectory = CheckWOTDirectory(opts.Output);

            if (string.IsNullOrEmpty(wotDirectory))
            {
                return 1;
            }

            var googleDrive = new GoogleDrive(modPackName);

            coloredConsoleWriteLine(ConsoleColor.Yellow, "Install ModPack '" + modPackName + "'");

            var root = ExecuteLongTask("Get ModPack Directory...", () => { return googleDrive.GetModPackDirectory(); });

            if (root == null)
            {
                coloredConsoleWriteLine(ConsoleColor.Red, "ERROR --> ModPack not found: " + opts.ModPackName);
                return 1;
            }

            var dir = Path.Combine(wotDirectory, "res_mods");

            if (Directory.Exists(dir))
            {
                Console.WriteLine("Delete Directory '" + dir + "' (y/n)?");
                var key = Console.ReadKey(true);

                if (key.Key == ConsoleKey.N)
                {
                    Console.WriteLine("User canceled install!");
                    return 1;
                }

                Directory.Delete(dir, true);
            }


            var list = ExecuteLongTask("Getting Modpacks '" + modPackName + "' Files...", () => { return googleDrive.GetModPackFiles(); });

            ExecuteLongTaskProgressbar(list, (f) =>
            {
                string sourcePath = GoogleDrive.GetSourcePathValue(f);
                if (string.IsNullOrEmpty(sourcePath))
                {
                    return;
                }

                if (f.MimeType == "application/vnd.google-apps.folder")
                {
                    var directoryInfo = new DirectoryInfo(Path.Combine(wotDirectory, sourcePath));
                    if (!directoryInfo.Exists)
                        directoryInfo.Create();
                }
                else
                {
                    var fileInfo = new FileInfo(Path.Combine(wotDirectory, sourcePath));
                    if (!fileInfo.Directory.Exists)
                        fileInfo.Directory.Create();

                    Trace.WriteLine("Downloading " + f.Title + " to : " + fileInfo.FullName);

                    googleDrive.DownloadFile(f, fileInfo.FullName);

                }
            });


            Console.WriteLine("Finished Installation of " + modPackName);

            return 0;
        }

        private static int UpdateModPack(UpdateOptions opts)
        {
            var googleDrive = new GoogleDrive(opts.ModPackName);

            string wotDirectory = CheckWOTDirectory(opts.Output);

            if (string.IsNullOrEmpty(wotDirectory))
            {
                return 1;
            }

            coloredConsoleWriteLine(ConsoleColor.Yellow, string.Format("Update ModPack '" + opts.ModPackName + "' {0}", opts.Remote ? "remote" : "local"));
            coloredConsoleWriteLine(ConsoleColor.Yellow, "Delete Files :" + opts.Delete);

            if (opts.Start)
            {
                coloredConsoleWriteLine(ConsoleColor.Yellow, "Starting World of Tanks after Update");
            }

            var root = ExecuteLongTask("Get ModPack Directory...", () => { return googleDrive.GetModPackDirectory(); });

            if (root == null)
            {
                coloredConsoleWriteLine(ConsoleColor.Red, "ERROR --> ModPack not found: " + opts.ModPackName);
                return 1;
            }

            var files = ExecuteLongTask("Getting local Files...", () => { return WotFiles.GetWotFiles(wotDirectory); });
            var remoteFiles = ExecuteLongTask("Getting remote Files...", () => { return googleDrive.GetModPackFiles(); });

            Console.WriteLine("Total Files: remote {0} local {1}", remoteFiles.Count, files.Count);

            var list = ExecuteLongTask("Comparing Files...", () => { return new CompareEngine(files, remoteFiles, opts.Remote, wotDirectory).Compare(); });

            var listAdd = list.Where(c => c.State == ResultState.Add).ToList();
            var listUpdate = list.Where(c => c.State == ResultState.Update).ToList();
            var listDelete = list.Where(c => c.State == ResultState.Delete).ToList();

            var color = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("");
            Console.WriteLine("Files to Add   : {0}", listAdd.Count);
            Console.WriteLine("Files to Update: {0}", listUpdate.Count);
            Console.WriteLine("Files to Delete: {0}", listDelete.Count);
            Console.WriteLine("Files equal    : {0}", opts.Remote ? files.Count - listUpdate.Count - listAdd.Count : remoteFiles.Count - listUpdate.Count - listAdd.Count);
            Console.WriteLine("");
            Console.ForegroundColor = color;

            int count = opts.Delete ? list.Count : listAdd.Count + listUpdate.Count;

            if (count == 0)
            {
                Console.WriteLine("Nothing to update.");

                StartWOT(opts, wotDirectory);

                return 0;
            }

            if (!opts.Ask)
            {

                Console.WriteLine("Do you really want to Update? (y/n)?");
                var key = Console.ReadKey(true);

                if (key.Key != ConsoleKey.Y)
                {
                    Console.WriteLine("User canceled update!");

                    StartWOT(opts, wotDirectory);

                    return 1;
                }
            }

            //Add
            if (listAdd.Count > 0)
            {
                Console.WriteLine("Adding Files...");
                ExecuteLongTaskProgressbar(listAdd, (c) =>
                   {
                       if (opts.Remote)
                       {
                           Trace.WriteLine("Uploading " + c.LocalFile.FullName);
                           googleDrive.UploadFile(c.LocalFile, wotDirectory);
                       }
                       else
                       {
                           if (!c.LocalFile.Directory.Exists)
                               c.LocalFile.Directory.Create();

                           Trace.WriteLine("Downloading " + c.RemoteFile.Title + " to : " + c.LocalFile.FullName);
                           googleDrive.DownloadFile(c.RemoteFile, c.LocalFile.FullName);

                       }
                   });
            }


            //Update
            if (listUpdate.Count > 0)
            {
                Console.WriteLine("Update Files...");
                ExecuteLongTaskProgressbar(listUpdate, (c) =>
                {
                    if (opts.Remote)
                    {
                        Trace.WriteLine("Deleting remote File:" + c.RemoteFile.Title);
                        googleDrive.DeleteFile(c.RemoteFile);
                        Trace.WriteLine("Uploading " + c.LocalFile.FullName);
                        googleDrive.UploadFile(c.LocalFile, wotDirectory);

                    }
                    else
                    {
                        if (!c.LocalFile.Directory.Exists)
                            c.LocalFile.Directory.Create();

                        if (!c.LocalFile.Exists)
                            c.LocalFile.Delete();

                        Trace.WriteLine("Downloading " + c.RemoteFile.Title + " to : " + c.LocalFile.FullName);
                        googleDrive.DownloadFile(c.RemoteFile, c.LocalFile.FullName);

                    }
                });
            }

            if (opts.Delete)
            {
                //Delete
                if (listDelete.Count > 0)
                {
                    Console.WriteLine("Delete Files...");
                    ExecuteLongTaskProgressbar(listDelete, (c) =>
                    {
                        if (opts.Remote)
                        {
                            Trace.WriteLine("Deleting remote File:" + c.RemoteFile.Title);
                            googleDrive.DeleteFile(c.RemoteFile);

                        }
                        else
                        {

                            Trace.WriteLine("Deleting File: " + c.LocalFile.FullName);
                            if (c.LocalFile.Exists)
                                c.LocalFile.Delete();
                        }
                    });
                }
            }


            StartWOT(opts, wotDirectory);

            return 0;
        }

        private static int RenameModPack(RenameOptions opts)
        {
            var googleDrive = new GoogleDrive(opts.ModPackName);

            coloredConsoleWriteLine(ConsoleColor.Yellow, string.Format("Rename ModPack '" + opts.ModPackName + "' To {0}", opts.NewModPackName));

            var directory = ExecuteLongTask("Get ModPack Directory...", () => { return googleDrive.GetOrCreateModPackDirectory(); });

            googleDrive.UpdateProperty(directory.Id, GoogleDrive.PropertyModPackName, opts.NewModPackName, "PRIVATE");
            googleDrive.RenameFile(directory.Id, opts.NewModPackName);

            var remoteFiles = ExecuteLongTask("Getting remote Files...", () => { return googleDrive.GetModPackFiles(); });

            ExecuteLongTaskProgressbar(remoteFiles, (f) =>
            {
                googleDrive.UpdateProperty(f.Id, GoogleDrive.PropertyModPackName, opts.NewModPackName, "PRIVATE");
            });

            return 0;

        }

        private static void StartWOT(UpdateOptions opts,string wotDirectory)
        {
            if (opts.Start)
            {
                try
                {
                    WotFiles.StartWOT(wotDirectory);
                }
                catch (FileNotFoundException ex)
                {
                    coloredConsoleWriteLine(ConsoleColor.Red, "ERROR --> WotLauncher not found in '" + ex.Message + "'");
                }
            }
        }

        //private static int ShareModPack(ShareOptions opts)
        //{
        //    var googleDrive = new GoogleDrive(opts.ModPackName);

        //    coloredConsoleWriteLine(ConsoleColor.Yellow, string.Format("Share ModPack '" + opts.ModPackName + "' To {0} with Role {1}", opts.Email, opts.Role));

        //    var root = ExecuteLongTask("Get ModPack Directory...", () => { return googleDrive.GetModPackDirectory(); });

        //    if (root == null)
        //    {
        //        coloredConsoleWriteLine(ConsoleColor.Red, "ERROR --> ModPack not found: " + opts.ModPackName);
        //        return 1;
        //    }

        //    googleDrive.ShareFile(root.Id, opts.Email, opts.Role);

        //    return 0;
        //}

        //private static int RemoveShareModPack(RemoveShareOptions opts)
        //{

        //    var googleDrive = new GoogleDrive(opts.ModPackName);

        //    coloredConsoleWriteLine(ConsoleColor.Yellow, string.Format("Remove Share ModPack '" + opts.ModPackName + "' To {0} with Role {1}", opts.Email, opts.Role));

        //    var root = ExecuteLongTask("Get ModPack Directory...", () => { return googleDrive.GetModPackDirectory(); });

        //    if (root == null)
        //    {
        //        coloredConsoleWriteLine(ConsoleColor.Red, "ERROR --> ModPack not found: " + opts.ModPackName);
        //        return 1;
        //    }
        //    var permissions = ExecuteLongTask("Get Permissions...", () => { return googleDrive.GetPermissionsOnFile(root.Id); });


        //    foreach (var p in permissions)
        //    {
        //        if (p.EmailAddress == opts.Email && p.Role == opts.Role)
        //        {
        //            Console.WriteLine("Delete Permission Role {0} on {1}", p.Role, p.EmailAddress);
        //            googleDrive.DeletePermission(root.Id, p.Id);
        //        }
        //        else if (string.IsNullOrEmpty(opts.Email) && p.Role == opts.Role)
        //        {
        //            Console.WriteLine("Delete Permission Role {0} on {1}", p.Role, p.EmailAddress);
        //            googleDrive.DeletePermission(root.Id, p.Id);
        //        }
        //        else if (string.IsNullOrEmpty(opts.Email) && string.IsNullOrEmpty(opts.Role))
        //        {
        //            Console.WriteLine("Delete Permission Role {0} on {1}", p.Role, p.EmailAddress);
        //            googleDrive.DeletePermission(root.Id, p.Id);
        //        }

        //    }

        //    return 0;
        //}



        #endregion
    }

    public class ConsoleSpiner
    {
        int counter;
        public ConsoleSpiner()
        {
            counter = 0;
        }
        public void Turn()
        {
            counter++;
            switch (counter % 4)
            {
                case 0: Console.Write("/"); break;
                case 1: Console.Write("-"); break;
                case 2: Console.Write("\\"); break;
                case 3: Console.Write("|"); break;
            }
            Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
        }
    }


}
